{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "udacity-cs344-hw6",
      "version": "0.3.2",
      "provenance": [],
      "collapsed_sections": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/depctg/udacity-cs344-colab/blob/master/notebook/udacity_cs344_hw6.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "metadata": {
        "id": "hse6gSyUS5ka",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# Homework 6 for Udacity CS344 Course, Intro to Parallel Programming\n",
        "# clone the code repo,\n",
        "!git clone https://github.com/depctg/udacity-cs344-colab\n",
        "!pip install git+git://github.com/depctg/nvcc4jupyter.git\n",
        "\n",
        "# load cuda plugin\n",
        "%config NVCCPluginV2.static_dir = True\n",
        "%config NVCCPluginV2.relative_dir = \"udacity-cs344-colab/src/HW6\"\n",
        "%load_ext nvcc_plugin\n",
        "\n",
        "# change to work directory, generate makefiles\n",
        "!mkdir udacity-cs344-colab/build\n",
        "%cd udacity-cs344-colab/build\n",
        "!cmake ../src"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "3vA0JP15TORh",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "%%cuda --name student_func.cu\n",
        "\n",
        "//Udacity HW 6\n",
        "//Poisson Blending\n",
        "\n",
        "/* Background\n",
        "   ==========\n",
        "\n",
        "   The goal for this assignment is to take one image (the source) and\n",
        "   paste it into another image (the destination) attempting to match the\n",
        "   two images so that the pasting is non-obvious. This is\n",
        "   known as a \"seamless clone\".\n",
        "\n",
        "   The basic ideas are as follows:\n",
        "\n",
        "   1) Figure out the interior and border of the source image\n",
        "   2) Use the values of the border pixels in the destination image\n",
        "      as boundary conditions for solving a Poisson equation that tells\n",
        "      us how to blend the images.\n",
        "\n",
        "      No pixels from the destination except pixels on the border\n",
        "      are used to compute the match.\n",
        "\n",
        "   Solving the Poisson Equation\n",
        "   ============================\n",
        "\n",
        "   There are multiple ways to solve this equation - we choose an iterative\n",
        "   method - specifically the Jacobi method. Iterative methods start with\n",
        "   a guess of the solution and then iterate to try and improve the guess\n",
        "   until it stops changing.  If the problem was well-suited for the method\n",
        "   then it will stop and where it stops will be the solution.\n",
        "\n",
        "   The Jacobi method is the simplest iterative method and converges slowly -\n",
        "   that is we need a lot of iterations to get to the answer, but it is the\n",
        "   easiest method to write.\n",
        "\n",
        "   Jacobi Iterations\n",
        "   =================\n",
        "\n",
        "   Our initial guess is going to be the source image itself.  This is a pretty\n",
        "   good guess for what the blended image will look like and it means that\n",
        "   we won't have to do as many iterations compared to if we had started far\n",
        "   from the final solution.\n",
        "\n",
        "   ImageGuess_prev (Floating point)\n",
        "   ImageGuess_next (Floating point)\n",
        "\n",
        "   DestinationImg\n",
        "   SourceImg\n",
        "\n",
        "   Follow these steps to implement one iteration:\n",
        "\n",
        "   1) For every pixel p in the interior, compute two sums over the four neighboring pixels:\n",
        "      Sum1: If the neighbor is in the interior then += ImageGuess_prev[neighbor]\n",
        "             else if the neighbor in on the border then += DestinationImg[neighbor]\n",
        "\n",
        "      Sum2: += SourceImg[p] - SourceImg[neighbor]   (for all four neighbors)\n",
        "\n",
        "   2) Calculate the new pixel value:\n",
        "      float newVal= (Sum1 + Sum2) / 4.f  <------ Notice that the result is FLOATING POINT\n",
        "      ImageGuess_next[p] = min(255, max(0, newVal)); //clamp to [0, 255]\n",
        "\n",
        "\n",
        "    In this assignment we will do 800 iterations.\n",
        "   */\n",
        "\n",
        "\n",
        "\n",
        "#include \"utils.h\"\n",
        "#include <thrust/host_vector.h>\n",
        "\n",
        "void your_blend(const uchar4* const h_sourceImg,  //IN\n",
        "                const size_t numRowsSource, const size_t numColsSource,\n",
        "                const uchar4* const h_destImg, //IN\n",
        "                uchar4* const h_blendedImg) //OUT\n",
        "{\n",
        "\n",
        "  /* To Recap here are the steps you need to implement\n",
        "\n",
        "     1) Compute a mask of the pixels from the source image to be copied\n",
        "        The pixels that shouldn't be copied are completely white, they\n",
        "        have R=255, G=255, B=255.  Any other pixels SHOULD be copied.\n",
        "\n",
        "     2) Compute the interior and border regions of the mask.  An interior\n",
        "        pixel has all 4 neighbors also inside the mask.  A border pixel is\n",
        "        in the mask itself, but has at least one neighbor that isn't.\n",
        "\n",
        "     3) Separate out the incoming image into three separate channels\n",
        "\n",
        "     4) Create two float(!) buffers for each color channel that will\n",
        "        act as our guesses.  Initialize them to the respective color\n",
        "        channel of the source image since that will act as our intial guess.\n",
        "\n",
        "     5) For each color channel perform the Jacobi iteration described\n",
        "        above 800 times.\n",
        "\n",
        "     6) Create the output image by replacing all the interior pixels\n",
        "        in the destination image with the result of the Jacobi iterations.\n",
        "        Just cast the floating point values to unsigned chars since we have\n",
        "        already made sure to clamp them to the correct range.\n",
        "\n",
        "      Since this is final assignment we provide little boilerplate code to\n",
        "      help you.  Notice that all the input/output pointers are HOST pointers.\n",
        "\n",
        "      You will have to allocate all of your own GPU memory and perform your own\n",
        "      memcopies to get data in and out of the GPU memory.\n",
        "\n",
        "      Remember to wrap all of your calls with checkCudaErrors() to catch any\n",
        "      thing that might go wrong.  After each kernel call do:\n",
        "\n",
        "      cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n",
        "\n",
        "      to catch any errors that happened while executing the kernel.\n",
        "  */\n",
        "}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "sSAnpiE2nL1T",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# make the cuda project\n",
        "!make HW6\n",
        "print(\"\\n====== RESULT OF HW6 =======\\n\")\n",
        "!bin/HW6 ../src/HW6/source.png ../src/HW6/blended.gold"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "4Zbj4MbVUVxq",
        "colab_type": "code",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# plot output images\n",
        "import matplotlib.pyplot as plt\n",
        "_,ax = plt.subplots(2,3, dpi=150)\n",
        "\n",
        "ax[0][0].imshow(plt.imread(\"../src/HW6/source.png\"))\n",
        "ax[0][0].set_title(\"original\")\n",
        "ax[0][0].grid(False)\n",
        "\n",
        "ax[0][2].imshow(plt.imread(\"HW6_output.png\"))\n",
        "ax[0][2].set_title(\"output\")\n",
        "ax[0][2].grid(False)\n",
        "\n",
        "ax[1][0].imshow(plt.imread(\"HW6_reference.png\"))\n",
        "ax[1][0].set_title(\"reference\")\n",
        "ax[1][0].grid(False)\n",
        "\n",
        "ax[1][1].imshow(plt.imread(\"HW6_differenceImage.png\"))\n",
        "ax[1][1].set_title(\"difference\")\n",
        "ax[1][1].grid(False)\n",
        "\n",
        "plt.show()"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}